using Unity.Burst;
using Unity.Collections;
using Unity.Jobs;
using Unity.Mathematics;

// 使用 Burst 编译器优化此 Job，指定浮点精度为标准，模式为快速（可能牺牲一些精度换取速度）
// 布置地雷
[BurstCompile(FloatPrecision.Standard, FloatMode.Fast)]
struct PlaceMinesJob : IJob
{
    // 表示游戏网格，包含地雷和数字信息
    public Grid grid;

    // 地雷的总数量
    public int mines;

    // 随机数生成的种子，用于保证地雷放置的可重复性（可选）
    public int seed;

    // Job 的执行入口点
    public void Execute ()
    {
        // 将已揭开的格子数量重置为 0（可能在游戏重新开始时使用）
        grid.RevealedCellCount = 0;

        // 获取网格中总的格子数量
        int candidateCount = grid.CellCount;

        // 创建一个临时的 NativeArray，用于存储所有格子的索引，作为候选位置
        // 这些索引将被随机打乱以决定地雷放置的位置
        var candidates = new NativeArray<int>(
            candidateCount, Allocator.Temp, NativeArrayOptions.UninitializedMemory
        );

        // 初始化网格：将所有格子设为初始状态（CellState.Zero）
        for (int i = 0; i < grid.CellCount; i++)
        {
            grid[i] = CellState.Zero;          // 每个格子初始为无地雷、数字为 0 的状态
            candidates[i] = i;                 // 候选数组中保存的是格子的索引，从 0 开始递增
        }

        // 使用给定的种子初始化一个随机数生成器，确保每次使用相同种子时地雷分布一致
        Random random = new Random((uint)seed);

        // 循环布置指定数量的地雷
        for (int m = 0; m < mines; m++)
        {
            // 从剩余的候选格子中随机选择一个索引
            // candidateCount 递减，保证每个地雷只会放置一次，不会重复
            int candidateIndex = random.NextInt(candidateCount--);

            // 在选中的格子上设置地雷
            SetMine(candidates[candidateIndex]);

            // 将选中的候选格子与当前最后一个未选择的候选格子交换，
            // 然后减少 candidateCount，这样下次就不会再选中已经放置地雷的格子
            candidates[candidateIndex] = candidates[candidateCount];
        }
    }

    /// <summary>
    /// 设置某个索引对应的格子为地雷，并更新其周围格子的数字
    /// </summary>
    void SetMine (int i)
    {
        // 将索引为 i 的格子设置为包含地雷的状态（通过 With 方法，可能是位操作或枚举组合）
        grid[i] = grid[i].With(CellState.Mine);

        // 获取该格子在六边形/三角形网格中的行和列坐标（假设是六边形网格，因为有特殊的行偏移逻辑）
        grid.GetRowColumn(i, out int r, out int c);

        // 更新地雷周围六个方向相邻格子的数字（表示周围地雷的数量）
        Increment(r - 1, c);     // 上方
        Increment(r + 1, c);     // 下方
        Increment(r, c - 1);     // 左侧
        Increment(r, c + 1);     // 右侧

        // 六边形网格中，根据列的奇偶性，左右上下的连接方式不同，因此需要特殊处理斜对角方向
        // 如果列号为偶数，使用 +1 的行偏移；如果为奇数，使用 -1 的行偏移
        int rowOffset = (c & 1) == 0 ? 1 : -1;

        // 增加两个斜对角方向的格子数字（六边形网格中特有的相邻方向）
        Increment(r + rowOffset, c - 1); // 左上或右上（取决于奇偶）
        Increment(r + rowOffset, c + 1); // 右上或左上（取决于奇偶）
    }

    // 增加指定行列位置格子的数字（如果该位置是一个有效的格子）
    // 该数字表示这个格子周围有多少个地雷
    void Increment (int r, int c)
    {
        // 尝试根据行列坐标获取对应的格子索引，如果该位置在网格范围内，则返回 true 和对应的索引
        if (grid.TryGetCellIndex(r, c, out int i))
        {
            // 如果该格子存在，则将其值加 1（表示附近有一个新的地雷）
            grid[i] += 1;
        }
        // 如果该位置不在网格内（比如边界之外），则什么都不做
    }
}